package com.ke.rule.base;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Constructor;

/**
 * Template of service methods based on decision tables
 * 
 * @author guyu
 *
 * @param <R>
 */
public abstract class DecisionBasedServiceMethod<R> extends ServiceMethodTemplate<R> {
  private static final Logger logger = LoggerFactory.getLogger(DecisionBasedServiceMethod.class);
  
  private Object value;     // 输入参数值，方法处理过程中值会发生变化
  private Object rawValue;  // 输入参数值，方法处理过程中保持原值不变
  private Object oldValue;      // 针对修改操作，修改前的对象（比如：从db中加载而来的老数据）
  private Object mergedValue;   // 针对修改操作，将客户端传入的增量数据与从db中加载而来的老数据进行合并而来的对象值
  private Object reloadedValue; // 调用reload方法后的值
  
  protected DecisionBasedServiceMethod(IApplicationService service) {
    this(service, null);
  }
  
  protected DecisionBasedServiceMethod(IApplicationService service, Object value) {
    this(service, value, null);
  }
  
  protected DecisionBasedServiceMethod(IApplicationService service, Object value, Object oldValue) {
    super(service);
    this.value = value;
    this.rawValue = copy(value);
    this.oldValue = oldValue;
    this.mergedValue = value;
  }
  
  public Object getValue() {
    return value;
  }

  protected void setValue(Object value) {
    this.value = value;
  }

  public Object getRawValue() {
    return rawValue;
  }
  
  public Object getOldValue() {
    return oldValue;
  }

  protected void setOldValue(Object oldValue) {
    this.oldValue = oldValue;
  }

  public Object getMergedValue() {
    return mergedValue;
  }

  protected void setMergedValue(Object mergedValue) {
    this.mergedValue = mergedValue;
  }
  
  public Object getReloadedValue() {
    return reloadedValue;
  }

  protected void setReloadedValue(Object reloadedValue) {
    this.reloadedValue = reloadedValue;
  }

  /**
   * Gets the decision table for the current method. A method may use more than one decision table.
   * It is recommended that a method use only one decision table, but if there is a reuse relationship between methods, the common can be extracted into a separate decision table
   * 
   * @return
   */
  public abstract String[] getDecisionTableFiles();
  
  /**
   * Can the FuncUnit bill resulting from the decision be cached?
   * @param stepType
   * @return
   */
  public boolean isFuncUnitBillCacheable(MethodStepType stepType) {
    return false;
  }
  
  /**
   * 复制一个全新对象
   * @param source
   * @return
   */
  @SuppressWarnings("unchecked")
  protected <T> T copy(Object source) {
    if (null == source) {
      return null;
    }
    
    Object target = null;
    try {
      Constructor<?> c = source.getClass().getDeclaredConstructor();
      c.setAccessible(true);
      target = c.newInstance();
      BeanUtils.copyProperties(source, target);
    } catch (Exception e) {
      logger.error("failed to copy the input parameter: " + source.getClass(), e);
    }
    return (T) (null == target ? source : target);
  }
}
